home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 August: Tool Chest / Dev.CD Aug 98 TC.toast / Sample Code / Networking / OTStreamLogViewer1.0b1 / LogEngine / LogEngine.c next >
Encoding:
C/C++ Source or Header  |  1998-03-19  |  11.6 KB  |  381 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        LogEngine.c
  3.  
  4.     Contains:    The core code to talk to the STREAMS log module.
  5.  
  6.     Written by:    Quinn "The Eskimo!"
  7.  
  8.     Copyright:    © 1998 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.     You may incorporate this sample code into your applications without
  13.     restriction, though the sample code has been provided "AS IS" and the
  14.     responsibility for its operation is 100% yours.  However, what you are
  15.     not permitted to do is to redistribute the source as "DSC Sample Code"
  16.     after having made changes. If you're going to re-distribute the source,
  17.     we require that you make it clear in the source that the code was
  18.     descended from Apple Sample Code, but that you've made changes.
  19. */
  20.  
  21. #define qDebug 1
  22.  
  23. /////////////////////////////////////////////////////////////////////
  24. // Pick up the standard C stuff.
  25.  
  26. #import <stdio.h>
  27. #import <string.h>
  28.  
  29. /////////////////////////////////////////////////////////////////////
  30. // Pick up low-level OT APIs.
  31.  
  32. #import <OTDebug.h>
  33. #import <stropts.h>
  34. #import <mistream.h>
  35.  
  36. /////////////////////////////////////////////////////////////////////
  37. // Pick up the symbolic name of the various OT modules.
  38.  
  39. #import <modnames.h>
  40.  
  41. /////////////////////////////////////////////////////////////////////
  42. // OTDebugStr is not defined in any OT header files, but it is
  43. // exported by the libraries, so we define the prototype here.
  44.  
  45. extern pascal void OTDebugStr(const char* str);
  46.  
  47. /////////////////////////////////////////////////////////////////////
  48. // Pick up our types and prototypes.
  49.  
  50. #import "LogEngine.h"
  51.  
  52. /////////////////////////////////////////////////////////////////////
  53.  
  54. // If we can't log an entry (because of lack of memory), we
  55. // increment gDroppedLogEntries.  The client can access this to
  56. // tell the user that we're dropped logs.
  57.  
  58. static UInt32 gDroppedLogEntries = 0;
  59.  
  60. // gNewLogEntries is a FIFO containing log entries that have
  61. // been read but not yet delivered to the client.  It is populated
  62. // at interrupt time by our notifier (LogNotifier) and consumed
  63. // at system task time by ForEachNewLogEntry.
  64.  
  65. static OTLIFO gNewLogEntries = { nil };
  66.  
  67. static SInt32 gNumberInList = 0;
  68.  
  69. // We record the PSN of the current process when we start up
  70. // so that we can wake ourselves up when log entries arrive.
  71. // We also have a flag to say whether we've already called
  72. // WakeUpProcess, so we don't call it zillions of times.
  73.  
  74. static ProcessSerialNumber gOurProcess;
  75. static OTLock gProcessWoken;
  76.  
  77. /////////////////////////////////////////////////////////////////////
  78.  
  79. static void CreateNewLogEntry(struct log_ctl *logHeader, OTBuffer *logTextBuffer)
  80.     // Create a new LogEntry, fill out the field fields based on
  81.     // logHeader and the variable length data based on the text in
  82.     // logTextBuffer, and add it to the gNewEntries list of recent
  83.     // additions.
  84.     //
  85.     // Context: OT deferred task
  86. {
  87.     OSStatus junk;
  88.     OTBuffer *thisTextBuffer;
  89.     UInt32 textLength;
  90.     LogEntryPtr newEntry;
  91.     UInt8 *destCursor;
  92.  
  93.     // First calculate the number of bytes of text that are in
  94.     // this entry (ie in the logTextBuffer and any buffers chained
  95.     // off it).
  96.     
  97.     textLength = 0;
  98.     thisTextBuffer = logTextBuffer;
  99.     while ( thisTextBuffer != nil ) {
  100.         OTAssert("CreateNewLogEntry: Non-data message in buffer chain", thisTextBuffer->fType == M_DATA);
  101.         textLength += thisTextBuffer->fLen;
  102.         thisTextBuffer = thisTextBuffer->fNext;
  103.     }
  104.  
  105.     // Then allocate a new log entry of the correct size,
  106.     // ie the length of the fixed part + the length of the text.
  107.     
  108.     newEntry = (LogEntryPtr) OTAllocMem( sizeof(LogEntry) + textLength );
  109.     if (newEntry == nil) {
  110.         if (gDroppedLogEntries == 0) {
  111.             // OTDebugStr("CreateNewLogEntry: Started dropping log entries");
  112.         }
  113.         OTAtomicAdd32(1, (SInt32 *) &gDroppedLogEntries);
  114.     } else {
  115.         
  116.         // Fill out the fixed fields at the beginning of the
  117.         // new log entry.
  118.         
  119.         newEntry->fNext = nil;
  120.         newEntry->fMagic = kMagicValue;
  121.         newEntry->fRefCount = 1;
  122.         newEntry->fTextLength = textLength;
  123.         newEntry->fLogHeader = *logHeader;
  124.         
  125.         // Now copy bytes out of the text messages into the
  126.         // variable part of the log entry.  destCursor is the
  127.         // byte we're going to blat next, ie it starts just
  128.         // after the fixed fields of the new log entry and marches 
  129.         // onwards through the variable text data.
  130.         
  131.         destCursor = ((UInt8 *) newEntry) + sizeof(LogEntry);
  132.         
  133.         thisTextBuffer = logTextBuffer;
  134.         while ( thisTextBuffer != nil ) {
  135.             BlockMoveData(thisTextBuffer->fData, destCursor, thisTextBuffer->fLen);
  136.             destCursor += thisTextBuffer->fLen;
  137.             thisTextBuffer = thisTextBuffer->fNext;
  138.         }
  139.         OTAssert("CreateNewLogEntry: Did not copy the number of bytes that we calculated were in the message",
  140.                     destCursor = ((UInt8 *) newEntry) + sizeof(LogEntry) + textLength);
  141.         
  142.         // Finally add the new entry to the global list
  143.         // of new entries to be processed at SystemTask time.
  144.         
  145.         OTLIFOEnqueue(&gNewLogEntries, (OTLink *) &newEntry->fNext);
  146.         
  147.         OTAtomicAdd32(1, &gNumberInList);
  148.         
  149.         // If we haven't already woken up our process, do so now.
  150.         
  151.         if ( OTAcquireLock(&gProcessWoken) ) {
  152.             // OTDebugStr("CreateNewLogEntry: Waking up process");
  153.  
  154.             junk = WakeUpProcess(&gOurProcess);
  155.             OTAssert("CreateNewLogEntry: Error waking up our process", junk == noErr);
  156.         }
  157.     }
  158. }
  159.  
  160. extern pascal void RetainLogEntry(LogEntryPtr thisEntry)
  161.     // See comment in interface part.
  162. {
  163.     OTAssert("RetainLogEntry: Bad magic", thisEntry->fMagic == kMagicValue);
  164.     thisEntry->fRefCount += 1;
  165. }
  166.  
  167. extern pascal void ReleaseLogEntry(LogEntryPtr thisEntry)
  168.     // See comment in interface part.
  169. {
  170.     OTAssert("ReleaseLogEntry: Bad magic", thisEntry->fMagic == kMagicValue);
  171.     thisEntry->fRefCount -= 1;
  172.     if ( thisEntry->fRefCount == 0 ) {
  173.         thisEntry->fMagic = 'bad!';
  174.         OTFreeMem(thisEntry);
  175.     }
  176. }
  177.  
  178. /////////////////////////////////////////////////////////////////////
  179.  
  180. static pascal void LogNotifier(StreamRef strm, OTEventCode code, OTResult err, void *cookie)
  181.     // This is the notifier that OT calls when an event happens on
  182.     // the raw stream.  In this case the only event we're interested in
  183.     // is SIGPOLL, which says that something interesting has happened with
  184.     // data, typically that data has arrived.  We respond to a SIGPOLL
  185.     // by calling OTReadMessage to extract the message from the stream.
  186.     // We then process the message and loop looking for more.
  187.     //
  188.     // OTReadMessage is like getmsg() but it a) operates immediately,
  189.     // so we don't need to wait for a completion event like we do with
  190.     // getmsg, and b) returns the data without copying it.
  191.     //
  192.     // Context: OT deferred task
  193. {
  194.     #pragma unused(err)
  195.     #pragma unused(cookie)
  196.     OTReadInfo readInfo;
  197.     OTBuffer *readBuffer;
  198.  
  199.     // Debugger();
  200.         
  201.     switch (code) {
  202.         case kSIGNALEVENT + SIGPOLL:
  203.             do {
  204.                 readInfo.fType = kOTAnyMsgType;
  205.                 readInfo.fCommand = 0;                // Not strictly necessary because kOTAnyMsgType implies this field is ignored.
  206.                 readBuffer = OTReadMessage(strm, &readInfo);
  207.                 if ( readBuffer != nil ) {
  208.                     if ( (readBuffer->fType == M_PROTO || readBuffer->fType == M_PCPROTO) &&
  209.                              readBuffer->fLen == sizeof(struct log_ctl) ) {
  210.                         CreateNewLogEntry( (struct log_ctl *) readBuffer->fData , readBuffer->fNext);
  211.                     } else {
  212.                         OTDebugStr("LogNotifier: OTReadMessage returned a weird buffer");
  213.                     }
  214.                     OTReleaseBuffer(readBuffer);
  215.                 }
  216.             } while ( readBuffer != nil );
  217.             break;
  218.         default:
  219.             OTDebugStr("LogNotifier: Unexpected event code");
  220.             break;
  221.     }
  222. }
  223.  
  224. /////////////////////////////////////////////////////////////////////
  225.  
  226. static OSStatus SetupLogging(StreamRef strm, int_t command, 
  227.                             struct trace_ids traceInfo[], UInt32 traceInfoCount)
  228.     // Sends a log configuration ioctl to the raw stream.
  229.     // command is the ioctl, traceInfoCount is the number of
  230.     // trace_ids in the buffer pointed to be traceInfo.  Note
  231.     // that traceInfoCount can be zero, a common case for setting
  232.     // up an error log stream.  Note that strm is assumed to be
  233.     // in sync/blocking mode.
  234.     //
  235.     // Context: SystemTask /only/
  236. {
  237.     OSStatus err;
  238.     struct strioctl logIoctl; 
  239.     
  240.     logIoctl.ic_cmd = command;
  241.     logIoctl.ic_timout = -1;
  242.     logIoctl.ic_dp = (char *) traceInfo;
  243.     logIoctl.ic_len = traceInfoCount * sizeof(struct trace_ids);
  244.  
  245.     err = OTStreamIoctl(strm, I_STR, &logIoctl);
  246.     
  247.     return err;
  248. }
  249.  
  250. /////////////////////////////////////////////////////////////////////
  251. // Public Entry Points
  252.  
  253. // This is the raw stream which we use for logging. It is
  254. // set up by StartLogging and torn down by StopLogging.
  255.  
  256. static StreamRef gLogStream = kOTInvalidStreamRef;
  257.  
  258. extern pascal OSStatus StartLogging(Boolean logErrors,
  259.                                 UInt32 traceInfoCount, struct trace_ids traceInfo[])
  260.  
  261.     // See comment in interface part.
  262. {
  263.     OSStatus err;
  264.     OSStatus junk;
  265.  
  266.     OTAssert("StartLogging: paramErr", traceInfoCount == 0 || traceInfo != nil);
  267.     
  268.     OTAssert("StartLogging: We're already logging!", gLogStream == kOTInvalidStreamRef);
  269.  
  270.     OTClearLock(&gProcessWoken);
  271.     
  272.     err = GetCurrentProcess(&gOurProcess);
  273.     
  274.     // Create and configure a stream to the log device.  Note that
  275.     // we do this stuff with the stream in sync/blocking mode, and
  276.     // switch to async/blocking at the end.
  277.  
  278.     if (err == noErr) {
  279.         gLogStream = OTStreamOpen(MI_LOG_DEVICE, 0, &err);
  280.     }
  281.     if (err == noErr) {
  282.         OTStreamSetBlocking(gLogStream);
  283.     }
  284.     if (err == noErr && traceInfoCount > 0) {
  285.     
  286.         // If we've been asked to log traces, tell the log device that
  287.         // we're the trace stream.
  288.         
  289.         err = SetupLogging(gLogStream, I_TRCLOG, traceInfo, traceInfoCount);
  290.     }
  291.     if (err == noErr && logErrors) {
  292.  
  293.         // If we've been asked to log error, tell the log device that
  294.         // we're the error stream.
  295.  
  296.         err = SetupLogging(gLogStream, I_ERRLOG, nil, 0);
  297.     }
  298.     if (err == noErr) {
  299.         
  300.         // We want all signals to arrive in our notifier.  The only one we're
  301.         // specifically interested in is SIGPOLL (ie S_RDNORM etc) but if any
  302.         // other signals are generated we /really/ want to know about it.
  303.         
  304.         err = OTStreamIoctl(gLogStream, I_SETSIG,
  305.                         (void *) (S_INPUT | S_RDNORM | S_RDBAND | S_HIPRI | S_OUTPUT | S_WRNORM | 
  306.                         S_WRBAND | S_MSG | S_ERROR | S_HANGUP | S_BANDURG));
  307.     }
  308.     if (err == noErr) {
  309.         OTStreamSetAsynchronous(gLogStream);
  310.         err = OTStreamInstallNotifier(gLogStream, LogNotifier, gLogStream);
  311.     }
  312.     
  313.     // Clean up.
  314.     
  315.     if (err != noErr) {
  316.         junk = OTStreamClose(gLogStream);
  317.         OTAssert("StartLogging: OTStreamClose failed", junk == noErr);
  318.         gLogStream = kOTInvalidStreamRef;
  319.     }
  320.     
  321.     return err;
  322. }
  323.  
  324. extern pascal void StopLogging(void)
  325.     // See comment in interface part.
  326. {
  327.     OSStatus junk;
  328.     
  329.     OTAssert("StopLogging: We're not logging!", gLogStream != kOTInvalidStreamRef);
  330.  
  331.     junk = OTStreamClose(gLogStream);
  332.     OTAssert("StopLogging: OTStreamClose failed", junk == noErr);
  333.     gLogStream = kOTInvalidStreamRef;
  334. }
  335.  
  336. extern pascal Boolean LoggingActive(void)
  337.     // See comment in interface part.
  338. {
  339.     return gLogStream != kOTInvalidStreamRef;
  340. }
  341.  
  342. extern pascal UInt32 NumberOfDroppedLogEntries(void)
  343.     // See comment in interface part.
  344. {
  345.     return gDroppedLogEntries;
  346. }
  347.  
  348. extern pascal void ForEachNewLogEntry(ProcessLogEntryProcPtr doThis, void *refCon)
  349.     // See comment in interface part.
  350. {
  351.     OTLink *thisLink;
  352.     LogEntryPtr thisEntry;
  353.     SInt32 junkLong;
  354.  
  355.     if ( gNumberInList != 0 ) {
  356.         // OTDebugStr("ForEachNewLogEntry: Process woken with something to do");
  357.     }
  358.  
  359.     OTClearLock(&gProcessWoken);
  360.  
  361.     // If you're having troubles understanding this, check out
  362.     // the sample code in the section on lists in "Inside Macintosh:
  363.     // Networking with Open Transport".
  364.         
  365.     do {
  366.         thisLink = OTReverseList(OTLIFOStealList(&gNewLogEntries));
  367.         while ( thisLink != nil ) {
  368.             thisEntry = OTGetLinkObject(thisLink, LogEntry, fNext);
  369.             
  370.             doThis(thisEntry, refCon);
  371.  
  372.             thisLink = thisLink->fNext;
  373.  
  374.             ReleaseLogEntry(thisEntry);
  375.  
  376.             junkLong = OTAtomicAdd32(-1, (SInt32 *) &gNumberInList);
  377.             OTAssert("ForEachNewLogEntry: ", junkLong >= 0);
  378.         }
  379.     } while ( gNewLogEntries.fHead != nil );
  380. }
  381.